package dao

import (
	"editor/constant"
	"editor/model"
	"editor/store"
	"github.com/gin-gonic/gin"
	"path"
	"sort"
	"time"
)

type MDFileInfo struct {
	ID           int64  `xorm:"id"`
	MdTitle      string `xorm:"mdTitle"`
	MdContent    string `xorm:"mdContent"`
	MdFileSize   int64  `xorm:"mdFileSize"`
	CategoryID   int64  `xorm:"categoryId"`
	CategoryName string `xorm:"categoryName"`
	IsShow       int64  `xorm:"isShow"`
	CreateTime   int64  `xorm:"createTime"`
	UpdateTime   int64  `xorm:"updateTime"`
}

type MDDirInfo struct {
	Id           int64  `xorm:"id"`
	CategoryName string `xorm:"categoryName"`
	SortNum      int64  `xorm:"sortNum"`
	IsShow       int64  `xorm:"isShow"`
	CreateTime   int64  `xorm:"createTime"`
	UpdateTime   int64  `xorm:"updateTime"`
}

type MDListItem struct {
	Id           int64         `xorm:"id"`
	MdTitle      string        `xorm:"mdTitle"`
	MdFilePath   string        `xorm:"mdFilePath"`
	MdFileSize   int64         `xorm:"mdFileSize"`
	SortNum      int64         `xorm:"sortNum"`
	CategoryID   int64         `xorm:"categoryId"`
	CategoryName string        `xorm:"categoryName"`
	IsCategory   int64         `xorm:"isCategory"`
	IsShow       int64         `xorm:"isShow"`
	CreateTime   int64         `xorm:"createTime"`
	UpdateTime   int64         `xorm:"updateTime"`
	ChildList    []*MDListItem `xorm:"-"`
}

func GetListItem4Index(c *gin.Context, isAll bool) ([]*MDListItem, error) {
	var list []*MDListItem
	session := store.DB().Table(model.TableNameMdFile).
		Select("id,mdTitle,mdFilePath,mdFileSize,sortNum,categoryId,categoryName,isCategory,isShow,createTime,updateTime").
		Where("isDelete=0")
	if !isAll {
		session = session.Where("isShow=1")
	}
	err := session.OrderBy("sortNum desc,id desc").Find(&list)
	if err != nil {
		store.Errorf(c, err, "GetListItemFailed")
		return nil, err
	}

	return formatList(list), nil
}

func GetListItem4Admin(c *gin.Context) ([]*MDListItem, error) {
	var list []*MDListItem
	err := store.DB().Table(model.TableNameMdFile).
		Select("id,mdTitle,mdFilePath,mdFileSize,sortNum,categoryId,categoryName,isCategory,isShow,createTime,updateTime").
		Where("isDelete=?", 0).
		OrderBy("sortNum desc,id desc").
		Find(&list)
	if err != nil {
		store.Errorf(c, err, "GetListItemFailed")
		return nil, err
	}

	return formatList(list), nil
}

func formatList(list []*MDListItem) (newList []*MDListItem) {
	if len(list) == 0 {
		return nil
	}

	subList := map[int64][]*MDListItem{}
	for _, li := range list {
		if li.CategoryID > 0 {
			subList[li.CategoryID] = append(subList[li.CategoryID], li)
		} else {
			newList = append(newList, li)
		}
	}

	for idx, m := range newList {
		if m.IsCategory == 1 {
			if arr, ok := subList[m.Id]; ok {
				newList[idx].ChildList = arr
			}
		}
	}

	sortList(newList)

	return
}

func sortList(newList []*MDListItem) {
	sort.SliceStable(newList, func(i, j int) bool {

		if len(newList[i].ChildList) > 0 {
			sort.SliceStable(newList[i].ChildList, func(m, n int) bool {
				return newList[i].ChildList[m].SortNum >= newList[i].ChildList[n].SortNum
			})
		}

		if len(newList[j].ChildList) > 0 {
			sort.SliceStable(newList[j].ChildList, func(m, n int) bool {
				return newList[j].ChildList[m].SortNum >= newList[j].ChildList[n].SortNum
			})
		}

		return newList[i].SortNum >= newList[j].SortNum
	})
}

func GetMDFileInfo(c *gin.Context, id int64) (info *MDFileInfo, err error) {
	info = new(MDFileInfo)
	_, err = store.DB().Table(model.TableNameMdFile).
		Select("id,mdTitle,mdContent,mdFileSize,categoryId,categoryName,isShow,createTime,updateTime").
		Where("id=? AND isDelete=?", id, 0).
		Get(info)

	if err != nil {
		store.Errorf(c, err, "GetMDFileInfoFailed")
		return nil, err
	}

	return
}

func GetMDDirInfo(c *gin.Context, id int64) (info *MDDirInfo, err error) {
	info = new(MDDirInfo)
	_, err = store.DB().Table(model.TableNameMdFile).
		Select("id,categoryName,sortNum,isShow,createTime,updateTime").
		Where("id=? AND isDelete=?", id, 0).
		Get(info)

	if err != nil {
		store.Errorf(c, err, "GetMDDirInfoFailed")
		return nil, err
	}
	return
}

func UpdateMDFileContent(c *gin.Context, id int64, cons string) (err error) {
	_, err = store.DB().Table(model.TableNameMdFile).Where("id=?", id).Limit(1).
		Update(map[string]interface{}{"mdContent": cons, "updateTime": time.Now().Unix()})
	if err != nil {
		store.Errorf(c, err, "UpdateMDFileContent,Id:%d", id)
		return
	}

	return
}

func UpdateMDSort(c *gin.Context, id, sortNum int64) (err error) {
	_, err = store.DB().Table(model.TableNameMdFile).Where("id=?", id).Limit(1).
		Update(map[string]interface{}{"sortNum": sortNum, "updateTime": time.Now().Unix()})
	if err != nil {
		store.Errorf(c, err, "UpdateMDSort,Id:%d", id)
		return
	}

	return
}

func UpdateMDName(c *gin.Context, id int64, newName string) (err error) {
	var info = struct {
		Id           int64  `xorm:"id"`
		MdTitle      string `xorm:"mdTitle"`
		CategoryID   int64  `xorm:"categoryId"`
		CategoryName string `xorm:"categoryName"`
		IsCategory   int64  `xorm:"isCategory"`
	}{}
	isGet, err := store.DB().Table(model.TableNameMdFile).
		Select("id,mdTitle,categoryId,categoryName,isCategory").
		Where("id=? AND isDelete=0", id).Get(&info)
	if err != nil {
		store.Errorf(c, err, "getMdInfoFailed,Id:%d", id)
		return
	}
	if !isGet || info.Id <= 0 {
		store.Warningf(c, "getMdInfoEmpty,Id:%d", id)
		return
	}

	if !(info.IsCategory == 1) {
		if info.MdTitle == newName {
			store.Infof(c, "mdTitle not change,id:%d,name:%s", id, newName)
			return
		}
		_, err = store.DB().Table(model.TableNameMdFile).Where("id=?", id).Limit(1).
			Update(map[string]interface{}{"mdTitle": newName, "updateTime": time.Now().Unix()})
		if err != nil {
			store.Errorf(c, err, "UpdateMDSort,Id:%d", id)
			return
		}
	} else {
		if info.CategoryName == newName {
			store.Infof(c, "categoryName not change,id:%d,name:%s", id, newName)
			return
		}
		_, err = store.DB().Table(model.TableNameMdFile).Where("id=?", id).Limit(1).
			Update(map[string]interface{}{"categoryName": newName, "updateTime": time.Now().Unix()})
		if err != nil {
			store.Errorf(c, err, "UpdateMDNameFailed,Id:%d", id)
			return
		}
	}
	return
}

func CheckTitleIsRepeat(c *gin.Context, title string, categoryId, currentId int64) (isRepeat bool, err error) {
	session := store.DB().Table(model.TableNameMdFile).
		Select("id,mdTitle,mdContent,mdFileSize,categoryId,categoryName,isShow,createTime,updateTime").
		Where("mdTitle=? and categoryId=?", title, categoryId)
	if currentId > 0 {
		session = session.Where("id!=?", currentId)
	}
	var info = new(MDFileInfo)
	isGet, err := session.Get(info)
	if err != nil {
		store.Errorf(c, err, "CheckTitleIsRepeat failed title=%s and categoryId=%d", title, categoryId)
		return
	}
	if isGet && info.ID > 0 {
		return true, nil
	} else {
		return false, nil
	}
}

func CheckCategoryIsRepeat(c *gin.Context, dirname string, currentId int64) (isRepeat bool, err error) {
	session := store.DB().Table(model.TableNameMdFile).
		Select("id,categoryName,isShow,createTime,updateTime").
		Where("categoryName=?", dirname)
	if currentId > 0 {
		session = session.Where("id!=?", currentId)
	}
	var info = new(MDDirInfo)
	isGet, err := session.Get(info)
	if err != nil {
		store.Errorf(c, err, "CheckCategoryIsRepeat failed dirname=%s and currentId=%d", dirname, currentId)
		return
	}
	if isGet && info.Id > 0 {
		return true, nil
	} else {
		return false, nil
	}
}

func AddFile(c *gin.Context, title string, categoryId int64, content string, sortNum int64) (id int64, err error) {
	var (
		categoryInfo *MDDirInfo
		mdFilePath   string
		categoryName string
	)
	if categoryId > 0 {
		categoryInfo, err = GetMDDirInfo(c, categoryId)
		if err != nil {
			store.Errorf(c, err, "GetMDDirInfo failed categoryId=%d", categoryId)
			return 0, err
		}
		if categoryInfo != nil {
			mdFilePath = path.Join(constant.MDFileDir, categoryInfo.CategoryName, title+".md")
			categoryName = categoryInfo.CategoryName
		} else {
			mdFilePath = path.Join(constant.MDFileDir, title+".md")
		}
	} else {
		mdFilePath = path.Join(constant.MDFileDir, title+".md")
	}
	now := time.Now().Unix()
	obj := &model.MdFile{
		MdTitle:      title,
		MdContent:    content,
		MdFilePath:   mdFilePath,
		MdFileSize:   int64(len(content)),
		SortNum:      sortNum,
		CategoryID:   categoryId,
		CategoryName: categoryName,
		IsCategory:   0,
		IsShow:       1,
		IsDelete:     0,
		CreateTime:   now,
		UpdateTime:   now,
	}

	err = obj.Add(c)
	return obj.Id, err
}

func AddDir(c *gin.Context, dirName string, sortNum int64) (id int64, err error) {
	now := time.Now().Unix()
	obj := &model.MdFile{
		MdTitle:      "",
		MdContent:    "",
		MdFilePath:   "",
		MdFileSize:   0,
		SortNum:      sortNum,
		CategoryID:   0,
		CategoryName: dirName,
		IsCategory:   1,
		IsShow:       1,
		IsDelete:     0,
		CreateTime:   now,
		UpdateTime:   now,
	}

	err = obj.Add(c)
	return obj.Id, err
}
